package org.someone.formular;

import java.math.BigDecimal;
import java.util.Queue;
import java.util.Stack;
import java.util.concurrent.LinkedBlockingQueue;
/**
 * 
 * @author Ray.
 *
 */
public class Calcstra {
static String operator = "+-*/%^()";	//操作符 
	
	public static BigDecimal getCalcstraValue(String strEval) throws Exception{
		return CalcstraWithQueue(getQueue(strEval.trim()));
	}
	
	/** 
     * 预处理表达式，正、负号前加0(如果一个加号（减号）出现在最前面或左括号后面，则该加号（减号）为正负号)  比如  -1-(-1+1) 这种表达式 会处理成 0-1-(0-1+1) 
     */  
    private static String pretreatment(String str) {  
        StringBuffer sb = new StringBuffer(str);  
        for (int i = 0; i < sb.length(); i++) {  
            char c = sb.charAt(i);  
            if (operator.indexOf(c) >= 0) {  
                if (i == 0) {  
                    sb.insert(0, '0');  
                    i++;  
                } else if (sb.charAt(i - 1) == '(' && c != '(') {  
                    sb.insert(i, '0');  
                    i++;  
                } else if (sb.charAt(i - 1) == '+' && c == '-') {
                	sb.insert(i, '0');  
                	i++;
                } else if (sb.charAt(i - 1) == '-' && c == '-') {
                	sb.deleteCharAt(i);
                	sb.setCharAt(i-1, '+');
                	i--;
                }
            }  
        }  
        return sb.toString();  
    }
    
    /*** 
     * 0 优先级相等 ； -1 op1 优先级大于op2； 1 op2 优先级大于 op1 
     */  
    private static int opcompare(char op1, char op2) {  
        if (op1 == '(') { // 遇到括号 就直接入栈 所以 op2 大  
            return 1;  
        }  
        if ('^' == op1) {  
            if (op2 == '^') {  
                return 0;  
            }  
            return -1;  
        } else if ("+-".indexOf(op1) >= 0) {  
            if ("+-".indexOf(op2) >= 0) {  
                return 0;  
            }  
            return 1;  
        } else // if("*/%".indexOf(op1) >=0) 没必要 再判断是否为 */% 了  
        {  
            if ("+-".indexOf(op2) >= 0) {  
                return -1;  
            } else if ('^' == op2) {  
                return 1;  
            }  
            return 0;  
        }  
    }  
    
    /** 
     *  计算 逆波兰表达式  用队列表示 
     * @throws Exception  各种错误都有可能 
     */  
    private static BigDecimal CalcstraWithQueue(Queue<String> que) throws Exception {  
        Stack<BigDecimal> stack = new Stack<BigDecimal>();  
        while(true)  
        {  
	        String str = que.poll();  
	        if(str == null)  
	        {  
	            break;  
	        }  
	        if (operator.indexOf(str) >= 0) {  
	              
	        	BigDecimal num2 = stack.pop();  
	        	BigDecimal num1 = stack.pop();  
	        	BigDecimal tempresult = BigDecimal.ZERO;  
	            switch (str.charAt(0)) {  
	            case '+':  
	                tempresult = num1.add(num2);//BigDecimal.valueOf(num1).add(BigDecimal.valueOf(num2)).doubleValue(); 
	                break;  
	            case '-':  
	                tempresult = num1.subtract(num2);//BigDecimal.valueOf(num1).subtract(BigDecimal.valueOf(num2)).doubleValue(); 
	                break;  
	            case '*':  
	                tempresult = num1.multiply(num2);//BigDecimal.valueOf(num1).multiply(BigDecimal.valueOf(num2)).doubleValue(); 
	                break;  
	            case '/':  
	                if(num2.doubleValue()==0)  
	                {  
	                    throw new Exception("出错:除数为 0");  
	                }  
	                tempresult = num1.divide(num2);//num1 / num2;  
	                break;  
	            case '%':  
	                tempresult = num1.remainder(num2);//BigDecimal.valueOf(num1).remainder(BigDecimal.valueOf(num2)).doubleValue();   
	                break;  
	            case '^':  
	                tempresult = num1.pow(num2.intValue());//Math.pow(num1, num2);  
	                break;  
	            default:  
	                throw new Exception("运算符: " + str + " 未识别!");  
	            }  
	            stack.push(tempresult);  
	        } else {  
//	            stack.push(new BigDecimal(Double.valueOf(str)));
                stack.push(new BigDecimal(str));
            }
        }  
        return stack.pop();  
    }  
    
    /** 
     * 取得堆栈
     */  
    private static Queue<String> getQueue(String s) throws Exception {  
        //预处理式子  
        String prestr = pretreatment(s);  
        //用于保存  逆波兰式 的队列   
        LinkedBlockingQueue<String> polish = new LinkedBlockingQueue<String>();  
        // 拼接 数字 char ---> numble  
        StringBuffer temp = new StringBuffer();  
          
        Stack<Character> stack = new Stack<Character>();  
        for (int i = 0; i < prestr.length(); i++) {  
  
            char c = prestr.charAt(i); 
            if(c==' ') continue;
            //如果找到 操作符  
            if (operator.indexOf(c) >= 0) {  
                if (temp.length() > 0) {//如果 有数字 就压栈  
                    polish.offer(temp.toString());  
                    temp = new StringBuffer();  
                }  
                switch (c) {  
                case '(':  
                    stack.push(c);  
                    break;  
                case ')':  
                    while (stack.size() > 0) {  
                        char op = stack.pop();  
                        if (op != '(') {  
                            polish.offer(String.valueOf(op));  
                        } else {  
                            break;  
                        }  
                    }  
                    break;  
                default:  
                    if (stack.size() == 0) {  
                        stack.push(c);  
                    } else {  
                        while (stack.size() > 0) {  
                            char op1 = stack.lastElement();  
                            int com = opcompare(op1, c);  
                            if (com <= 0) {  
                                polish.offer(String.valueOf(stack.pop()));  
                            } else {  
                                stack.push(c);  
                                break;  
                            }  
                        }  
                        if (stack.size() == 0) {  
                            stack.push(c);  
                        }  
                    }  
                    break;  
                }  
            } else {  
                temp.append(c);  
            }  
        }  
        if (temp.length() > 0) {  
            polish.offer(temp.toString());  
        }  
        while (stack.size() > 0) {  
            polish.offer(String.valueOf(stack.pop()));  
        }  
        return polish;  
    }  
}
